home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.qualcomm.com
/
2014.06.ftp.qualcomm.com.tar
/
ftp.qualcomm.com
/
eudora
/
developers
/
emsapi
/
carbon_emsapi.sit.hqx
/
Macintosh API Support
/
ctencode.c
< prev
next >
Wrap
Text File
|
2001-03-08
|
19KB
|
563 lines
/* ======================================================================
Function to encode and decode binary data to quoted-printable data.
Code extracted from Eudora. Based on Steve Dorner's code.
Filename: ctencode.c
Last Edited: March 7, 1997
Authors: Laurence Lundblade, Myra Callen
Copyright: 1995, 1996 QUALCOMM Inc.
Technical support: <emsapi-info@qualcomm.com>
*/
#include <string.h>
#include "ctencode.h"
#include "rfc822.h"
#define SKIP -1
#define FAIL -2
#define PAD -3
#define kNewLine "\p\r\n"
#define ABS(x) ((x)<0 ? -(x) : (x))
#define kBase64CStr "base64"
#define kQ_PCStr "quoted-printable"
#define k7BitCStr "7bit"
#define k8BitCStr "8bit"
#define kBinaryCStr "binary"
static UPtr gEncode =
(UPtr) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static short gDecode[] =
{
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,SKIP,SKIP,FAIL,FAIL,SKIP,FAIL,FAIL, /* 0 */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* 1 */
SKIP,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,0x3e,FAIL,FAIL,FAIL,0x3f, /* 2 */
0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,FAIL,FAIL,FAIL,PAD ,FAIL,FAIL, /* 3 */
FAIL,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e, /* 4 */
0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,FAIL,FAIL,FAIL,FAIL,FAIL, /* 5 */
FAIL,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, /* 6 */
0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,FAIL,FAIL,FAIL,FAIL,FAIL, /* 7 */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* 8 */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* 9 */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* A */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* B */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* C */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* D */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* E */
FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL, /* F */
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
};
/*
* the encoding is like this:
*
* Binary:<111111><222222><333333>
* 765432107654321076543210
* Encode:<1111><2222><3333><4444>
*/
/* Bit extracting macros */
#define Bot2(b) ((b)&0x3)
#define Bot4(b) ((b)&0xF)
#define Bot6(b) ((b)&0x3F)
#define Top2(b) Bot2((b)>>6)
#define Top4(b) Bot4((b)>>4)
#define Top6(b) Bot6((b)>>2)
/************************************************************************
* Encode64 - convert binary data to base64
* binPtr -> the binary data (or nil to close encoder)
* binLen -> the length of the binary data
* sixFourPtr -> pointer to buffer for the base64 data
* sixFourLen <- the length of the base64 data
* e64 <-> state; caller must preserve
* returns kCTEDoneOK or kCTEFailed
************************************************************************/
short Encode64(const UPtr binPtr, long binLen,
const UPtr sixFourPtr, long *sixFourBufSize, Enc64Ptr e64)
{
UPtr binSpot; /* the byte currently being decoded */
UPtr sixFourSpot = sixFourPtr; /* the spot to which to copy the encoded chars */
short bpl;
UPtr end, bPtr; /* end of integral decoding */
PStr newLine = kNewLine;
bpl = e64->bytesOnLine; /* in inner loop; want local copy */
bPtr = (UPtr) binPtr;
if (binLen) {
if (e64->partialCount) { // do we have any stuff left from last time?
short needMore = 3 - e64->partialCount;
if (binLen >= needMore) { // we can encode some bytes
BlockMoveData(bPtr, e64->partial + e64->partialCount, needMore);
binLen -= needMore;
bPtr += needMore;
do {
if ((bpl) == 68) {
int m_nl;
for (m_nl = 0; m_nl < *(newLine); )
*(sixFourSpot)++ = (newLine)[++m_nl];
(bpl) = 0;
}
(bpl) += 4;
*(sixFourSpot)++ = gEncode[Top6((e64->partial)[0])];
*(sixFourSpot)++ = gEncode[Bot2((e64->partial)[0]) << 4 |
Top4((e64->partial)[1])];
*(sixFourSpot)++ = gEncode[Bot4((e64->partial)[1]) << 2 |
Top2((e64->partial)[2])];
*(sixFourSpot)++ = gEncode[Bot6((e64->partial)[2])];
}
while (0);
if (*sixFourBufSize < sixFourSpot - sixFourPtr)
return(kCTEFailed);
e64->partialCount = 0;
}
/*
* if we don't have enough bytes to complete the leftovers, we
* obviously don't have 3 bytes. So the encoding code will fall
* through to the point where we copy the leftovers to the partial
* buffer. As long as we're careful to append and not copy blindly,
* we'll be fine.
*/
}
// we encode the integral multiples of three
end = bPtr + 3*(binLen/3);
for (binSpot = bPtr; binSpot < end; binSpot += 3) {
do {
if ((bpl)==68) {
int m_nl;
for (m_nl=0;m_nl<*(newLine);)
*(sixFourSpot)++ = (newLine)[++m_nl];
(bpl) = 0;
}
(bpl) += 4;
*(sixFourSpot)++ = gEncode[Top6((binSpot)[0])];
*(sixFourSpot)++ = gEncode[Bot2((binSpot)[0])<<4 | Top4((binSpot)[1])];
*(sixFourSpot)++ = gEncode[Bot4((binSpot)[1])<<2 | Top2((binSpot)[2])];
*(sixFourSpot)++ = gEncode[Bot6((binSpot)[2])];
}
while (0);
if (*sixFourBufSize < sixFourSpot - sixFourPtr)
return(kCTEFailed);
}
// now, copy the leftovers to the partial buffer
binLen = binLen % 3;
if (binLen) {
BlockMoveData(binSpot,e64->partial+e64->partialCount,binLen);
e64->partialCount += binLen;
}
}
else { // we've been called to cleanup the leftovers
if (e64->partialCount) {
if (e64->partialCount<2) e64->partial[1] = 0;
e64->partial[2] = 0;
do {
if ((bpl)==68) {
int m_nl;
for (m_nl=0;m_nl<*(newLine);)
*(sixFourSpot)++ = (newLine)[++m_nl];
(bpl) = 0;
}
(bpl) += 4;
*(sixFourSpot)++ = gEncode[Top6((e64->partial)[0])];
*(sixFourSpot)++ = gEncode[Bot2((e64->partial)[0])<<4 | Top4((e64->partial)[1])];
*(sixFourSpot)++ = gEncode[Bot4((e64->partial)[1])<<2 | Top2((e64->partial)[2])];
*(sixFourSpot)++ = gEncode[Bot6((e64->partial)[2])];
}
while (0);
if (*sixFourBufSize < sixFourSpot - sixFourPtr)
return(kCTEFailed);
// now, replace the unneeded bytes with ='s
sixFourSpot[-1] = '=';
if (e64->partialCount==1) sixFourSpot[-2] = '=';
}
}
e64->bytesOnLine = bpl; /* copy back to state buffer */
*sixFourBufSize = sixFourSpot - sixFourPtr; /* return actual buffer size */
return kCTEDoneOK;
}
/************************************************************************
* Decode64 - convert base64 data to binary
* sixFourPtr -> the base64 data (or nil to close decoder)
* sixFourLen -> the length of the base64 data
* binPtr -> pointer to buffer to hold binary data
* binBufSize <- length of binary data
* d64 <-> pointer to decoder state
* decErrCnt <- the number of decoding errors found
* returns kCTEDoneOK or kCTEFailed
************************************************************************/
short Decode64(const UPtr sixFourPtr, long sixFourLen,
const UPtr binPtr, long *binBufSize,
const Dec64Ptr d64, long *decErrCnt)
{
short decode; /* the decoded short */
Byte c; /* the decoded byte */
/* we separate the short & the byte so the compiler can worry about byteorder */
UPtr end = sixFourPtr + sixFourLen; /* stop decoding here */
UPtr binSpot = binPtr; /* current output character */
short decoderState; /* which of 4 bytes are we seeing now? */
long invalCount; /* how many bad chars found this time around? */
long padCount; /* how many pad chars found so far? */
Byte partial; /* partially decoded byte from/for last/next time */
Boolean wasCR;
UPtr pSixFour, sixFourStartPtr;
decoderState = d64->decoderState; // fetch state from caller's buffer
invalCount = 0; /* we'll add the invalCount to the buffer later */
padCount = d64->padCount;
partial = d64->partial;
wasCR = d64->wasCR;
pSixFour = (UPtr) sixFourPtr;
if (sixFourLen) {
sixFourStartPtr = pSixFour;
for ( ; pSixFour < end; pSixFour++) {
switch(decode=gDecode[*pSixFour]) {
case SKIP: break; /* skip whitespace */
case FAIL: invalCount++; break; /* count invalid characters */
case PAD: padCount++; break; /* count pad characters */
default:
if (padCount) { // found a non-pad character
invalCount += padCount;
padCount = 0; // prev pad was an error
}
c = decode; // extract the right bits
switch (decoderState) {
case 0:
partial = c<<2;
decoderState++;
break;
case 1:
*binSpot++ = partial|Top4(c);
partial = Bot4(c)<<4;
decoderState++;
break;
case 2:
*binSpot++ = partial|Top6(c);
partial = Bot2(c)<<6;
decoderState++;
break;
case 3:
*binSpot++ = partial|c;
decoderState=0;
break;
}
}
/* make sure we're not writing past the end of the buffer */
if (*binBufSize < binSpot - binPtr)
return(kCTEFailed);
} /* for pSixFour */
} /* if sixFourLen */
else { // all done. Did all end up evenly?
switch (decoderState) {
case 0:
invalCount += padCount; /* came out evenly, so should be no pads */
break;
case 1:
invalCount++; /* data missing */
invalCount += padCount; /* since data missing; should be no pads */
break;
case 2:
invalCount += ABS(padCount-2); /* need exactly 2 pads */
break;
case 3:
invalCount += ABS(padCount-1); /* need exactly 1 pad */
break;
}
}
*binBufSize = binSpot - binPtr; /* return actual buffer size */
// save state in caller's buffer
d64->decoderState = decoderState;
d64->invalCount += invalCount;
d64->padCount = padCount;
d64->partial = partial;
d64->wasCR = wasCR;
*decErrCnt = invalCount;
return kCTEDoneOK;
}
/************************************************************************
* EncodeQP - convert binary data to quoted-printable
* binPtr -> the binary data
* binLen -> the length of the binary data (or nil to close encoder)
* qpPtr -> pointer to buffer for the quoted-printable data
* qpBufSize <-> length of the quoted-printable buffer, returns actual length
* eQP <-> encoder state for bytes per line; caller must preserve
* returns kCTEDoneOK or kCTEFailed
************************************************************************/
long EncodeQP(const UPtr binPtr, long binLen,
const UPtr qpPtr, long *qpBufSize, long *eQP)
{
UPtr binSpot = binPtr; /* the byte currently being decoded */
UPtr qpSpot = qpPtr; /* the spot to which to copy the encoded chars */
UPtr binEndPtr; /* stop decoding line here */
short bpl, c;
static char *hex="0123456789ABCDEF";
Boolean encode;
UPtr nextSpace;
PStr newLine = kNewLine;
#define ROOMFOR(x) \
do { \
if (bpl + x > 76) { \
*qpSpot++ = '='; \
do { \
BlockMoveData(newLine+1,qpSpot,*newLine); \
qpSpot += *newLine; \
bpl = 0; \
} while (0); \
} \
} while(0)
bpl = *eQP; /* in inner loop; want local copy */
if (binLen && binPtr) {
binEndPtr = binPtr + binLen;
for (binSpot = binPtr; binSpot < binEndPtr; binSpot++) {
/* fail if we try to write beyond the output buffer size */
if (*qpBufSize + 4 < qpSpot - qpPtr)
return(kCTEFailed);
c = *binSpot; // make copy of char
if (c == newLine[1]) // handle newlines
do {
BlockMoveData(newLine+1,qpSpot,*newLine);
qpSpot += *newLine;
bpl = 0;
} while (0);
else if ((c == '\n') || (c == '\r'))
; /* skip other newline characters */
else {
if ((c == ' ') || (c == '\t')) { /* trailing white space */
encode = ((binSpot == binEndPtr - 1) || (binSpot[1] == '\r'));
if (!encode) {
for(nextSpace = binSpot + 1; nextSpace < binEndPtr; nextSpace++)
if ((*nextSpace == ' ') || (*nextSpace == '\r')) {
if ((nextSpace - binSpot) < 20) ROOMFOR(nextSpace - binSpot);
break;
}
}
}
else
encode = (c == '=') || (c < 33) || (c > 126); /* weird characters */
if (encode)
ROOMFOR(3);
else
ROOMFOR(1);
encode = encode || !bpl && ((c == 'F') || (c == '.'));
if (encode) {
*qpSpot++ = '=';
*qpSpot++ = hex[(c>>4)&0xf];
*qpSpot++ = hex[c&0xf];
bpl += 3;
}
else {
*qpSpot++ = c;
bpl++;
}
} /* else */
} /* for */
} /* if binLen */
*eQP = bpl; /* copy back to state buffer */
*qpBufSize = qpSpot - qpPtr; /* return actual buffer size */
return kCTEDoneOK;
}
#define HEX(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \
((c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : \
((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : -1)))
/************************************************************************
* DecodeQP - convert quoted printable data to binary
* qpStartPtr -> the quoted printable data
* qpLen -> the length of the quoted printable data (or nil to close decoder)
* binPtr -> pointer to buffer to hold binary data
* binBufSize <-> the length of the binary buffer, returns actual length
* dQP <-> pointer to decoder state; caller must preserve
* decErrCnt <- the number of decoding errors found
* returns kCTEDoneOK or kCTEFailed
* Note: this does not do conversion to local newline convention as the
* Eudora internal version does (Maybe a flag to select would be good?)
************************************************************************/
long DecodeQP(const UPtr qpPtr, long qpLen,
const UPtr binPtr, long *binBufSize,
const DecQPPtr dQP, long *decErrCnt)
{
Byte c; /* the decoded byte */
UPtr binSpot = binPtr; /* current output character */
UPtr qpSpot = qpPtr; /* current input character */
UPtr qpEndPtr; /* stop decoding line here */
QPStates state;
Byte lastChar;
short upperNib, lowerNib, errs = 0;
long spaceCnt, cnt;
state = dQP->state; // fetch state from caller's buffer
lastChar = dQP->lastChar;
spaceCnt = dQP->spaceCount;
if (qpLen && qpPtr) {
qpEndPtr = qpPtr + qpLen;
for (qpSpot = qpPtr; qpSpot < qpEndPtr; qpSpot++) {
c = *qpSpot;
switch (state) {
case qpNormal:
if (c == ' ') {
spaceCnt++;
}
else if (c == '=') {
for (cnt = 0; cnt < spaceCnt; cnt++)
*binSpot++ = ' ';
state = qpEqual;
spaceCnt = 0;
}
else if (c == '\r' || c == '\n') {
*binSpot++ = c;
spaceCnt = 0; // Ignore spaces before CR
}
else {
for (cnt = 0; cnt < spaceCnt; cnt++)
*binSpot++ = ' ';
*binSpot++ = c;
spaceCnt = 0;
}
break;
case qpEqual:
if (c == ' ') {
spaceCnt++;
}
else if (c == '\r' || c == '\n') {
state = qpNormal;
spaceCnt = 0; // Ignore spaces between Equal and CR
}
else
state = qpByte1;
break;
case qpByte1:
upperNib = HEX(lastChar);
lowerNib = HEX(c);
if ((upperNib < 0) || (lowerNib < 0))
errs++;
else
*binSpot++ = (upperNib<<4) | lowerNib;
state = qpNormal;
break;
}
lastChar = c;
} /* for */
} /* if qpLen */
else if (state != qpNormal)
errs++; /* when closing decoder */
// save state in caller's buffer
dQP->state = state;
dQP->lastChar = lastChar;
dQP->spaceCount = spaceCnt;
*binBufSize = binSpot - binPtr;
*decErrCnt = errs;
return kCTEDoneOK;
}
#pragma mark -
/************************************************************************
* RFC822_ParseCTE - parse Content-Transer-Encoding header line
* src -> Valid Transfer-Encoding header
* returns enumerated integer 'TrEncType' type specifying CTE.
************************************************************************/
TrEncType RFC822_ParseCTE(const char *src)
{
const unsigned short kPrefixStrLen = strlen(kCTEncodeHdrCStr);
char *cp = (char *) src + kPrefixStrLen, *mechanism = nil;
TrEncType cte = CTE_Error;
if (strncmp(src, kCTEncodeHdrCStr, kPrefixStrLen) == 0) { // Check prefix
// Get first token (skips whitespace/comments)
mechanism = RFC822_ExtractToken(&cp);
if ((mechanism) && (strlen(mechanism) > 0)) {
if (strcmp(mechanism, kBase64CStr) == 0)
cte = CTE_Base64;
else if (strcmp(mechanism, kQ_PCStr) == 0)
cte = CTE_QP;
else if (strcmp(mechanism, k7BitCStr) == 0)
cte = CTE_7bit;
else if (strcmp(mechanism, k8BitCStr) == 0)
cte = CTE_8bit;
else if (strcmp(mechanism, kBinaryCStr) == 0)
cte = CTE_Binary;
}
DisposePtr(mechanism);
}
return (cte);
}
/************************************************************************
* RFC822_MakeCTE - create Content-Transer-Encoding header line
* NOTE: The user of this function is responsible for freeing the
* returned string.
* mechanism -> enumerated integer 'TrEncType' type specifying CTE
* returns content tranfer encoding header line string.
************************************************************************/
Handle RFC822_MakeCTE(TrEncType mechanism)
{
char s[80];
Handle h;
strcpy(s, kCTEncodeHdrCStr);
strcat(s, " ");
switch (mechanism) {
case CTE_Base64: strcat(s, kBase64CStr); break;
case CTE_QP: strcat(s, kQ_PCStr); break;
case CTE_7bit: strcat(s, k7BitCStr); break;
case CTE_8bit: strcat(s, k8BitCStr); break;
case CTE_Binary: strcat(s, kBinaryCStr); break;
default:
return nil;
}
PtrToHand(s, &h, strlen(s));
return h;
}
/************************************************************************
* RFC822_ExtractCTE - finds and extracts the content transfer encoding
* header line from a full multi-lined header.
* All unfolding (removing newlines) is done before header line is returned.
* NOTE: The user of this function is responsible for freeing the
* returned string.
* pFullHeader -> pointer to a full RFC822 header, including newlines
* returns extracted header line string; dynamically allocated
************************************************************************/
char *RFC822_ExtractCTE(const char *pFullHeader)
{
return RFC822_ExtractHeader(pFullHeader, kCTEncodeHdrCStr);
}